package com.idega.jackrabbit.repository;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.logging.Level;
import javax.jcr.Binary;
import javax.jcr.Credentials;
import javax.jcr.LoginException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Value;
import javax.jcr.security.AccessControlPolicy;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.core.RepositoryImpl;
import org.apache.jackrabbit.core.config.RepositoryConfig;
import org.apache.jackrabbit.value.ValueFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import com.idega.builder.bean.AdvancedProperty;
import com.idega.core.accesscontrol.business.LoginDBHandler;
import com.idega.core.accesscontrol.data.LoginTable;
import com.idega.core.business.DefaultSpringBean;
import com.idega.core.file.util.MimeTypeUtil;
import com.idega.idegaweb.IWMainApplicationShutdownEvent;
import com.idega.jackrabbit.JackrabbitConstants;
import com.idega.jackrabbit.security.JackrabbitSecurityHelper;
import com.idega.repository.RepositoryService;
import com.idega.repository.authentication.AuthenticationBusiness;
import com.idega.repository.bean.RepositoryItemVersionInfo;
import com.idega.user.data.User;
import com.idega.util.ArrayUtil;
import com.idega.util.CoreConstants;
import com.idega.util.IOUtil;
import com.idega.util.StringHandler;
import com.idega.util.StringUtil;
/**
* Implementation of {@link RepositoryService}
*
* @author valdas
*
*/
@Service
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class JackrabbitRepository extends DefaultSpringBean implements RepositoryService, org.apache.jackrabbit.api.JackrabbitRepository, ApplicationListener {
private Repository repository;
@Autowired
private JackrabbitSecurityHelper securityHelper;
@Autowired
private AuthenticationBusiness authenticationBusiness;
@Override
public void initializeRepository(InputStream configStream, String repositoryName) throws Exception {
try {
repository = RepositoryImpl.create(RepositoryConfig.create(configStream, repositoryName));
} finally {
IOUtil.close(configStream);
}
}
@Override
public String[] getDescriptorKeys() {
return repository.getDescriptorKeys();
}
@Override
public boolean isStandardDescriptor(String key) {
return repository.isStandardDescriptor(key);
}
@Override
public boolean isSingleValueDescriptor(String key) {
return repository.isSingleValueDescriptor(key);
}
@Override
public Value getDescriptorValue(String key) {
return repository.getDescriptorValue(key);
}
@Override
public Value[] getDescriptorValues(String key) {
return repository.getDescriptorValues(key);
}
@Override
public String getDescriptor(String key) {
return repository.getDescriptor(key);
}
@Override
public Session login(Credentials credentials, String workspaceName) throws LoginException, NoSuchWorkspaceException, RepositoryException {
return repository.login(credentials, workspaceName);
}
@Override
public Session login(Credentials credentials) throws LoginException, RepositoryException {
return repository.login(credentials);
}
@Override
public Session login(String workspaceName) throws LoginException, NoSuchWorkspaceException, RepositoryException {
return repository.login(workspaceName);
}
@Override
public Session login() throws LoginException, RepositoryException {
return repository.login();
}
@Override
public void shutdown() {
if (repository instanceof org.apache.jackrabbit.api.JackrabbitRepository) {
((org.apache.jackrabbit.api.JackrabbitRepository) repository).shutdown();
}
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof IWMainApplicationShutdownEvent) {
this.shutdown();
}
}
@Override
public boolean uploadFileAndCreateFoldersFromStringAsRoot(String parentPath, String fileName, String fileContentString, String contentType)
throws RepositoryException {
try {
return uploadFile(parentPath, fileName, StringHandler.getStreamFromString(fileContentString), contentType, null) != null;
} catch (Exception e) {
getLogger().log(Level.WARNING, "Error uploading file: " + fileContentString + "\n to: " + parentPath + fileName, e);
}
return false;
}
@Override
public boolean uploadXMLFileAndCreateFoldersFromStringAsRoot(String parentPath, String fileName, String fileContentString) throws RepositoryException {
return uploadFileAndCreateFoldersFromStringAsRoot(parentPath, fileName, fileContentString, MimeTypeUtil.MIME_TYPE_XML);
}
@Override
public boolean uploadFileAndCreateFoldersFromStringAsRoot(String parentPath, String fileName, InputStream stream, String contentType) throws RepositoryException {
return uploadFile(parentPath, fileName, stream, contentType, null) != null;
}
@Override
public boolean uploadFile(String uploadPath, String fileName, String contentType, InputStream fileInputStream) throws RepositoryException {
return uploadFile(uploadPath, fileName, fileInputStream, contentType, getUser()) != null;
}
private Node uploadFile(String parentPath, String fileName, InputStream content, String mimeType, User user, AdvancedProperty... properties)
throws RepositoryException {
if (parentPath == null) {
getLogger().warning("Parent path is not defined!");
return null;
}
if (StringUtil.isEmpty(fileName)) {
getLogger().warning("File name is not defined!");
return null;
}
if (content == null) {
getLogger().warning("Input stream is invalid!");
return null;
}
if (!parentPath.endsWith(CoreConstants.SLASH)) {
parentPath = parentPath.concat(CoreConstants.SLASH);
}
Binary binary = null;
Session session = null;
try {
session = getSession(user);
VersionManager versionManager = session.getWorkspace().getVersionManager();
Node root = session.getRootNode();
Node folder = getFolderNode(root, parentPath);
Node file = getFileNode(folder, fileName, versionManager);
Node resource = getResourceNode(file);
if (resource == null) {
resource = file.addNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE);
}
mimeType = StringUtil.isEmpty(mimeType) ? MimeTypeUtil.resolveMimeTypeFromFileName(fileName) : mimeType;
mimeType = StringUtil.isEmpty(mimeType) ? MimeTypeUtil.MIME_TYPE_APPLICATION : mimeType;
resource.setProperty(JcrConstants.JCR_MIMETYPE, mimeType);
resource.setProperty(JcrConstants.JCR_ENCODING, CoreConstants.ENCODING_UTF8);
binary = ValueFactoryImpl.getInstance().createBinary(content);
resource.setProperty(JcrConstants.JCR_DATA, binary);
Calendar lastModified = Calendar.getInstance();
lastModified.setTimeInMillis(System.currentTimeMillis());
resource.setProperty(JcrConstants.JCR_LASTMODIFIED, lastModified);
session.save();
versionManager.checkin(file.getPath());
return file;
} finally {
if (binary != null) {
binary.dispose();
}
IOUtil.close(content);
logout(session);
}
}
@Override
public List<RepositoryItemVersionInfo> getVersions(String parentPath, String fileName) throws RepositoryException {
List<RepositoryItemVersionInfo> itemsInfo = new ArrayList<RepositoryItemVersionInfo>();
Session session = null;
try {
session = getSession(getUser());
Node root = session.getRootNode();
Node folder = getFolderNode(root, parentPath);
Node file = getFileNode(folder, fileName, null);
VersionManager vm = session.getWorkspace().getVersionManager();
VersionHistory vh = vm.getVersionHistory(file.getPath());
for (VersionIterator vi = vh.getAllVersions(); vi.hasNext();) {
Version version = vi.nextVersion();
for (NodeIterator nodeIter = version.getNodes(); nodeIter.hasNext();) {
Node n = nodeIter.nextNode();
Binary binary = getBinary(n);
if (binary != null) {
RepositoryItemVersionInfo info = new RepositoryItemVersionInfo();
info.setCreated(version.getCreated().getTime());
info.setId(version.getIdentifier());
info.setVersion(getVersion(version.toString()));
itemsInfo.add(info);
}
}
}
return itemsInfo;
} finally {
logout(session);
}
}
private Double getVersion(String nodePath) {
Double version = 1.0;
if (StringUtil.isEmpty(nodePath)) {
return version;
}
if (nodePath.indexOf(CoreConstants.SLASH) == -1) {
return version;
}
String versionId = null;
try {
versionId = nodePath.substring(nodePath.lastIndexOf(CoreConstants.SLASH) + 1);
return Double.valueOf(versionId);
} catch (IndexOutOfBoundsException e) {
getLogger().warning("Error while trying to get version from " + nodePath);
} catch (NumberFormatException e) {
getLogger().warning("Error while converting " + versionId + " to Double");
}
return version;
}
private Node getFolderNode(Node parent, String nodeName) throws RepositoryException {
return getNode(parent, nodeName, true, JcrConstants.NT_FOLDER, null);
}
private Node getFileNode(Node parent, String nodeName, VersionManager versionManager) throws RepositoryException {
return getNode(parent, nodeName, true, JcrConstants.NT_FILE, versionManager);
}
private Node getResourceNode(Node parent) throws RepositoryException {
return getNode(parent, JcrConstants.JCR_CONTENT, false, JcrConstants.NT_RESOURCE, null);
}
private Node getNode(Node parent, String nodeName, String type) throws RepositoryException {
return getNode(parent, nodeName, true, type, null);
}
private Node getNode(Node parent, String nodeName, boolean createIfNotFound, String type, VersionManager versionManager) throws RepositoryException {
if (nodeName.startsWith(CoreConstants.SLASH)) {
nodeName = nodeName.substring(1);
}
if (nodeName.endsWith(CoreConstants.SLASH)) {
nodeName = nodeName.substring(0, nodeName.length() - 1);
}
Node node = null;
boolean created = false;
try {
node = parent.getNode(nodeName);
} catch (PathNotFoundException e) {
created = true;
}
if (node == null && createIfNotFound) {
boolean noType = StringUtil.isEmpty(type);
if (noType) {
getLogger().warning("No type for node is provided");
}
String[] pathParts = nodeName.split(CoreConstants.SLASH);
for (int i = 0; i < pathParts.length; i++) {
String name = pathParts[i];
node = getNode(parent, name, false, type, versionManager);
if (node == null) {
node = noType ? parent.addNode(name) : parent.addNode(name, type);
setDefaultProperties(node);
}
if (node == null) {
throw new RepositoryException("Node " + name + " can not be added to " + parent);
}
parent = node;
}
}
if (!created && node != null && versionManager != null) {
versionManager.checkout(node.getPath());
}
return node;
}
private void setDefaultProperties(Node node) throws RepositoryException {
List<String> defaultMixins = Arrays.asList(
JcrConstants.MIX_VERSIONABLE,
JcrConstants.MIX_REFERENCEABLE
);
for (String mixin: defaultMixins) {
node.addMixin(mixin);
}
}
private Session getSessionAsRootUser() throws RepositoryException {
return getSession(null);
}
private Session getSession(User user) throws RepositoryException {
if (user == null) {
try {
user = securityHelper.getSuperAdmin();
} catch (Exception e) {
getLogger().severe("Administrator user can not be resolved!");
throw new RepositoryException(e);
}
}
if (user == null) {
throw new RepositoryException("User can not be identified!");
}
LoginTable loginTable = LoginDBHandler.getUserLogin(user);
Credentials credentials = getCredentials(Integer.valueOf(user.getId()), loginTable.getUserPasswordInClearText());
return repository.login(credentials);
}
private void logout(Session session) {
if (session != null) {
session.logout();
}
}
@Override
public InputStream getInputStream(String path) throws IOException, RepositoryException {
return getInputStream(path, getUser());
}
@Override
public InputStream getInputStreamAsRoot(String path) throws IOException, RepositoryException {
return getInputStream(path, null);
}
private InputStream getInputStream(String path, User user) throws IOException, RepositoryException {
if (StringUtil.isEmpty(path)) {
getLogger().warning("Path to resource is not defined!");
return null;
}
Session session = null;
try {
session = getSession(user);
Node root = session.getRootNode();
Node file = getNode(root, path, false, null, null);
if (file == null) {
getLogger().warning("Resource does not exist: " + path);
return null;
}
return getInputStream(getBinary(file));
} finally {
logout(session);
}
}
private Binary getBinary(Node file) throws RepositoryException {
if (file == null) {
return null;
}
Node resource = getResourceNode(file);
Property prop = resource == null ? null : resource.getProperty(JcrConstants.JCR_DATA);
return prop == null ? null : prop.getBinary();
}
private InputStream getInputStream(Binary data) throws IOException, RepositoryException {
if (data == null) {
getLogger().warning("Value is not set for resource!");
return null;
}
return data.getStream();
}
private User getUser() throws RepositoryException {
User user = getCurrentUser();
if (user == null) {
throw new RepositoryException("User must be logged in!");
}
return user;
}
@Override
public boolean deleteAsRootUser(String path) throws RepositoryException {
return delete(path, null);
}
@Override
public boolean delete(String path) throws RepositoryException {
return delete(path, getUser());
}
private boolean delete(String path, User user) throws RepositoryException {
if (StringUtil.isEmpty(path)) {
getLogger().warning("Resource is not defined!");
return false;
}
Session session = null;
try {
session = getSession(user);
Node root = session.getRootNode();
Node fileOrFolder = getNode(root, path, false, null, null);
if (fileOrFolder == null) {
getLogger().warning("Resource does not exist: " + path);
return false;
}
fileOrFolder.remove();
session.save();
return true;
} finally {
logout(session);
}
}
@Override
public boolean createFolder(String path) throws RepositoryException {
return createFolder(path, getUser());
}
@Override
public boolean createFolderAsRoot(String path) throws RepositoryException {
return createFolder(path, null);
}
private boolean createFolder(String path, User user) throws RepositoryException {
if (StringUtil.isEmpty(path)) {
getLogger().warning("Resource path is not defined!");
return false;
}
Session session = null;
try {
session = getSession(user);
Node root = session.getRootNode();
Node folder = getNode(root, path, JcrConstants.NT_FOLDER);
session.save();
return folder != null;
} finally {
logout(session);
}
}
@Override
public AccessControlPolicy[] applyAccessControl(String absPath, AccessControlPolicy[] policies) throws RepositoryException {
if (StringUtil.isEmpty(absPath) || ArrayUtil.isEmpty(policies)) {
getLogger().warning("Path (" + absPath + ") or the policies (" + policies + ") are invalid");
return null;
}
Session session = null;
try {
session = getSessionAsRootUser();
// TODO: finish up!
/*AccessControlManager acm = session.getAccessControlManager();
PrincipalManager pm = null;
if (session instanceof SessionImpl) {
pm = ((SessionImpl) session).getPrincipalManager();
}
for (AccessControlPolicyIterator policiesIter = acm.getApplicablePolicies(absPath); policiesIter.hasNext();) {
AccessControlPolicy acp = policiesIter.nextAccessControlPolicy();
if (!(acp instanceof AbstractACLTemplate)) {
continue;
}
AbstractACLTemplate acl = (AbstractACLTemplate) acp;
for (AccessControlPolicy policy: policies) {
if (policy instanceof IWAccessControlPolicy) {
IWAccessControlPolicy pol = (IWAccessControlPolicy) policy;
Map<String, List<String>> prinicpalsAndPolicies = pol.getPolicies();
for (Iterator<String> roleOrIdIter = prinicpalsAndPolicies.keySet().iterator(); roleOrIdIter.hasNext();) {
final String roleOrId = roleOrIdIter.next();
List<String> privilegies = prinicpalsAndPolicies.get(roleOrId);
Principal p = new Principal() {
@Override
public String getName() {
return roleOrId;
}
};
int index = 0;
Privilege[] privs = new Privilege[privilegies.size()];
for (String privilege: privilegies) {
privs[index] = acm.privilegeFromName(privilege);
index++;
}
acl.addAccessControlEntry(p, privs);
}
}
}
acm.setPolicy(absPath, acl);
}
session.save();*/
} finally {
logout(session);
}
return null;
}
private boolean isValidPath(String absolutePath) {
if (StringUtil.isEmpty(absolutePath) || absolutePath.indexOf(CoreConstants.SLASH) == -1) {
getLogger().warning("Invalid absolute path: " + absolutePath);
return false;
}
return true;
}
private String getParentPath(String absolutePath) {
return isValidPath(absolutePath) ? absolutePath.substring(0, absolutePath.lastIndexOf(CoreConstants.SLASH)) : null;
}
private String getNodeName(String absolutePath) {
return isValidPath(absolutePath) ? absolutePath.substring(absolutePath.lastIndexOf(CoreConstants.SLASH)) : null;
}
@Override
public Node updateFileContents(String absolutePath, InputStream fileContents, boolean createFile, AdvancedProperty... properties) throws RepositoryException {
return uploadFile(getParentPath(absolutePath), getNodeName(absolutePath), fileContents, null, getUser());
}
@Override
public Node updateFileContents(String absolutePath, InputStream fileContents, AdvancedProperty... properties) throws RepositoryException {
return updateFileContents(absolutePath, fileContents, Boolean.TRUE, properties);
}
@Override
public InputStream getFileContents(Node fileNode) throws IOException, RepositoryException {
Session session = null;
try {
session = getSession(getUser());
return getInputStream(getBinary(fileNode));
} finally {
logout(session);
}
}
@Override
public Node getNode(String absolutePath) throws RepositoryException {
return getNode(absolutePath, false, true);
}
private Node getNode(String absolutePath, boolean createIfNotFound, boolean useVersionManager) throws RepositoryException {
if (!isValidPath(absolutePath)) {
return null;
}
Session session = null;
try {
session = getSession(getUser());
Node root = session.getRootNode();
return getNode(root, absolutePath, createIfNotFound, null, useVersionManager ? session.getWorkspace().getVersionManager() : null);
} finally {
logout(session);
}
}
@Override
public boolean setProperties(Node node, AdvancedProperty... properties) throws RepositoryException {
Session session = null;
try {
session = getSession(getUser());
return setProperties(session, node, properties);
} finally {
logout(session);
}
}
private boolean setProperties(Session session, Node node, AdvancedProperty... properties) throws RepositoryException {
if (node == null || ArrayUtil.isEmpty(properties)) {
return false;
}
for (AdvancedProperty property: properties) {
node.setProperty(property.getId(), property.getValue());
}
if (session != null) {
session.save();
}
return true;
}
@Override
public String getRepositoryConstantFolderType() {
return JcrConstants.NT_FOLDER;
}
@Override
public String getWebdavServerURI() {
String appContext = getApplication().getApplicationContextURI();
if (appContext.endsWith(CoreConstants.SLASH)) {
appContext = appContext.substring(0, appContext.lastIndexOf(CoreConstants.SLASH));
}
return appContext.concat("/repository");//CoreConstants.WEBDAV_SERVLET_URI; // TODO use a constant
}
@Override
public Credentials getCredentials(String userName, String password) {
return getCredentials(userName, password, new AdvancedProperty[] {});
}
public Credentials getCredentials(int userId, String password) {
return getCredentials(String.valueOf(userId), password, new AdvancedProperty(JackrabbitConstants.REAL_USER_ID_USED, Boolean.TRUE.toString()));
}
private Credentials getCredentials(String userNameOrId, String password, AdvancedProperty... attributes) {
if (StringUtil.isEmpty(userNameOrId)) {
getLogger().warning("User name or ID is not defined!");
return null;
}
SimpleCredentials credentials = new SimpleCredentials(userNameOrId, StringUtil.isEmpty(password) ? CoreConstants.EMPTY.toCharArray() : password.toCharArray());
if (!ArrayUtil.isEmpty(attributes)) {
for (AdvancedProperty attribute: attributes) {
credentials.setAttribute(attribute.getId(), attribute.getValue());
}
}
return credentials;
}
@Override
public boolean generateUserFolders(String loginName) throws RepositoryException {
if (StringUtil.isEmpty(loginName)) {
getLogger().warning("User name is not defined");
return false;
}
String userPath = authenticationBusiness.getUserPath(loginName);
Node userNode = getNode(userPath, true, false);
if (userNode == null) {
throw new RepositoryException("Node at " + userPath + " can not be created!");
}
// TODO: set access rights for userNode
// if (!getExistence(userPath)) {
// WebdavResource user = getWebdavResourceAuthenticatedAsRoot(userPath);
// user.mkcolMethod();
// user.close();
// }
String userHomeFolder = getUserHomeFolderPath(loginName);
Node userHomeNode = getNode(userHomeFolder, true, false);
if (userHomeNode == null) {
throw new RepositoryException("Node at " + userHomeFolder + " can not be created!");
}
// TODO: set access rights for userHomeNode
// WebdavResource rootFolder = getWebdavResourceAuthenticatedAsRoot();
// String userFolderPath = getURI(getUserHomeFolderPath(loginName));
// rootFolder.mkcolMethod(userFolderPath);
// rootFolder.mkcolMethod(userFolderPath + FOLDER_NAME_DROPBOX);
// rootFolder.mkcolMethod(userFolderPath + FOLDER_NAME_PUBLIC);
// rootFolder.close();
try {
updateUserFolderPrivileges(loginName);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
private String getUserHomeFolderPath(String loginName) {
return JackrabbitConstants.PATH_USERS_HOME_FOLDERS + CoreConstants.SLASH + loginName;
}
private void updateUserFolderPrivileges(String loginName) throws IOException, IOException {
// TODO: implement!
// String userFolderPath = getURI(getUserHomeFolderPath(loginName));
//
// AuthenticationBusiness aBusiness = getAuthenticationBusiness();
// String userPrincipal = aBusiness.getUserURI(loginName);
//
// // user folder
// AccessControlList userFolderList = getAccessControlList(userFolderPath);
// // should be 'all' for the user himself
// List<AccessControlEntry> userFolderUserACEs = userFolderList.getAccessControlEntriesForUsers();
// AccessControlEntry usersPositiveAce = null;
// AccessControlEntry usersNegativeAce = null;
// boolean madeChangesToUserFolderList = false;
// // Find the ace
// for (Iterator<AccessControlEntry> iter = userFolderUserACEs.iterator(); iter.hasNext();) {
// AccessControlEntry ace = iter.next();
// if (ace.getPrincipal().equals(userPrincipal) && !ace.isInherited()) {
// if (ace.isNegative()) {
// usersNegativeAce = ace;
// } else {
// usersPositiveAce = ace;
// }
// }
// }
// if (usersPositiveAce == null) {
// usersPositiveAce = new AccessControlEntry(userPrincipal, false, false, false, null, AccessControlEntry.PRINCIPAL_TYPE_USER);
// userFolderList.add(usersPositiveAce);
// }
//
// if (!usersPositiveAce.containsPrivilege(IWSlideConstants.PRIVILEGE_ALL)) {
// if (usersNegativeAce != null && usersNegativeAce.containsPrivilege(IWSlideConstants.PRIVILEGE_ALL)) {
// // do nothing becuse this is not ment to reset permissions but
// // to set them in the first
// // first place and update for legacy reasons. If Administrator
// // has closed someones user folder
// // for some reason, this is not supposed to reset that.
// } else {
// usersPositiveAce.addPrivilege(IWSlideConstants.PRIVILEGE_ALL);
// madeChangesToUserFolderList = true;
//
// // temporary at least:
// usersPositiveAce.setInherited(false);
// usersPositiveAce.setInheritedFrom(null);
// // temporary ends
// }
// }
// if (madeChangesToUserFolderList) {
// storeAccessControlList(userFolderList);
// }
//
// // dropbox
// updateUsersDropboxPrivileges(userFolderPath);
//
// // public folder
// updateUsersPublicFolderPrivileges(userFolderPath);
}
@Override
public boolean getExistence(String absolutePath) throws RepositoryException {
if (!isValidPath(absolutePath)) {
return false;
}
Session session = null;
try {
session = getSessionAsRootUser();
Node root = session.getRootNode();
Node node = getNode(root, absolutePath, false, null, null);
return node != null;
} finally {
logout(session);
}
}
}